#include <windows.h>

#include "npapi.h"
#include "npfunctions.h"

#include <string>
#include <map>

#include "scriptable_methods.h"
#include "scriptable_object.h"
#include "common.h"

typedef std::map<std::string, ScriptableMethod> method_map_t;

method_map_t methods;

extern NPNetscapeFuncs* browserFuncs;

bool CheckinDocument(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered CheckinDocument");
	if(argCount != 4 && argCount != 3)
		return false;

	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;

	const NPVariant* docLocObj = &args[0];
	if(docLocObj->type != NPVariantType_String)
		return false;
	const NPVariant* ciTyObj = &args[1];
	if(ciTyObj->type != NPVariantType_Bool && ciTyObj->type != NPVariantType_Int32)
		return false;
	const NPVariant* ciCmObj = &args[2];
	if(ciCmObj->type != NPVariantType_String)
		return false;
	const NPVariant* keepCheckoutObj = argCount == 4 ? &args[3] : NULL;
	if (argCount == 4)
	{
		if(keepCheckoutObj->type != NPVariantType_String)
			return false;
	}

	WCHAR *docLocStr = (WCHAR *)calloc(docLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, docLocObj->value.stringValue.UTF8Characters, docLocObj->value.stringValue.UTF8Length,
		docLocStr ,docLocObj->value.stringValue.UTF8Length);
	_bstr_t documentLocation(docLocStr);

	int checkInType = ciTyObj->value.intValue;

	WCHAR *ciCmStr = (WCHAR *)calloc(ciCmObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, ciCmObj->value.stringValue.UTF8Characters, ciCmObj->value.stringValue.UTF8Length,
		ciCmStr ,ciCmObj->value.stringValue.UTF8Length);
	_bstr_t checkInComment(ciCmObj->value.stringValue.UTF8Characters);

	VARIANT_BOOL keepCheckout = argCount == 4 ? keepCheckoutObj->value.boolValue : false;

	newDoc->CheckinDocument(documentLocation, checkInType, checkInComment, keepCheckout);

	free(docLocStr);
	free(ciCmStr);
	TRACE("Exiting CheckinDocument");
	return true;
}

bool CheckoutDocumentPrompt(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered CheckoutDocumentPrompt");
	if(argCount != 3)
		return false;
	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;
	const NPVariant* docLocObj = &args[0];
	if(docLocObj->type != NPVariantType_String)
		return false;
	const NPVariant* editAfterObj = &args[1];
	if(editAfterObj->type != NPVariantType_Bool)
		return false;
	const NPVariant* progIdObj = &args[2];
	if(progIdObj->type != NPVariantType_String)
		return false;

	WCHAR *docLocStr = (WCHAR *)calloc(docLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, docLocObj->value.stringValue.UTF8Characters, docLocObj->value.stringValue.UTF8Length,
		docLocStr ,docLocObj->value.stringValue.UTF8Length);
	_bstr_t documentLocation(docLocStr);

	bool editAfterCheckout = editAfterObj->value.boolValue;

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->CheckoutDocumentPrompt(documentLocation, editAfterCheckout, progId);

	free(docLocStr);
	TRACE("Exiting CheckoutDocumentPrompt");
	return true;
}

bool DiscardLocalCheckout(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered DiscardLocalCheckout");
	if(argCount != 1)
		return false;

	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;

	const NPVariant* docLocObj = &args[0];
	if(docLocObj->type != NPVariantType_String)
		return false;

	WCHAR *docLocStr = (WCHAR *)calloc(docLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, docLocObj->value.stringValue.UTF8Characters, docLocObj->value.stringValue.UTF8Length,
		docLocStr ,docLocObj->value.stringValue.UTF8Length);
	_bstr_t documentLocation(docLocStr);

	newDoc->DiscardLocalCheckout(documentLocation);

	free(docLocStr);
	TRACE("Exiting DiscardLocalCheckout");
	return true;
}

bool ViewDocument(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered ViewDocument");
	if(argCount != 2)
		return false;

	IOWSNewDocument2* newDoc = GetOWSObject<IOWSNewDocument2>(object);
	if(!newDoc)
		return false;
	const NPVariant* hrefObj = &args[0];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* progIdObj = &args[1];
	if(progIdObj->type != NPVariantType_String)
		return false;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefObj->value.stringValue.UTF8Characters);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->ViewDocument(docLocation, progId);

	free(hrefStr);
	TRACE("Exiting ViewDocument");
	return true;
}

bool ViewDocument2(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered ViewDocument2");
	if(argCount != 3)
		return false;

	IOWSNewDocument2* newDoc = GetOWSObject<IOWSNewDocument2>(object);
	if(!newDoc)
		return false;

	const NPVariant* hrefObj = &args[1];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* progIdObj = &args[2];
	if(progIdObj->type != NPVariantType_String)
		return false;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefStr);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->ViewDocument2(0, docLocation, progId);

	free(hrefStr);
	TRACE("Exiting ViewDocument2");
	return true;
}

bool ViewDocument3(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered ViewDocument3");
	if(argCount != 4)
		return false;
	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;
	const NPVariant* hrefObj = &args[1];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* openTypeObj = &args[2];
	if(openTypeObj->type != NPVariantType_Int32)
		return false;
	const NPVariant* progIdObj = &args[3];
	if(progIdObj->type != NPVariantType_String)
		return false;

	int openType = openTypeObj->value.intValue;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefStr);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->ViewDocument3(0, docLocation, openType, progId);

	free(hrefStr);
	TRACE("Exiting ViewDocument3");
	return true;
}

bool EditDocument(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered EditDocument");
	if(argCount != 2)
		return false;

	IOWSNewDocument* newDoc = GetOWSObject<IOWSNewDocument>(object);
	if(!newDoc)
		return false;

	const NPVariant* hrefObj = &args[0];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* progIdObj = &args[1];
	if(progIdObj->type != NPVariantType_String)
		return false;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefStr);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->EditDocument(docLocation, progId);

	free(hrefStr);
	TRACE("Exiting EditDocument");

	return true;
}

bool EditDocument2(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered EditDocument2");
	if(argCount != 3)
		return false;

	IOWSNewDocument2* newDoc = GetOWSObject<IOWSNewDocument2>(object);
	if(!newDoc)
		return false;
	const NPVariant* hrefObj = &args[1];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* progIdObj = &args[2];
	if(progIdObj->type != NPVariantType_String)
		return false;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefStr);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->EditDocument2(0, docLocation, progId);

	free(hrefStr);
	TRACE("Exiting EditDocument2");
	return true;
}

bool EditDocument3(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered EditDocument3");
	if(argCount != 4)
		return false;
	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;
	const NPVariant* hrefObj = &args[1];
	if(hrefObj->type != NPVariantType_String)
		return false;
	const NPVariant* openTypeObj = &args[2];
	if(openTypeObj->type != NPVariantType_Int32)
		return false;
	const NPVariant* progIdObj = &args[3];
	if(progIdObj->type != NPVariantType_String)
		return false;

	int openType = openTypeObj->value.intValue;

	WCHAR *hrefStr = (WCHAR *)calloc(hrefObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, hrefObj->value.stringValue.UTF8Characters, hrefObj->value.stringValue.UTF8Length,
		hrefStr ,hrefObj->value.stringValue.UTF8Length);
	_bstr_t docLocation(hrefStr);

	_variant_t progId(progIdObj->value.stringValue.UTF8Characters);

	newDoc->EditDocument3(0, docLocation, openType, progId);

	free(hrefStr);
	TRACE("Exiting EditDocument3");
	return true;
}

bool CreateNewDocument(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered CreateNewDocument");
	if(argCount != 2)
		return false;
	
	IOWSNewDocument* newDoc = GetOWSObject<IOWSNewDocument>(object);
	if(!newDoc)
		return false;

	const NPVariant* templLocObj = &args[0];
	if(templLocObj->type != NPVariantType_String)
		return false;
	const NPVariant* defSaveLocObj = &args[1];
	if(defSaveLocObj->type != NPVariantType_String)
		return false;

	WCHAR *templLocStr = (WCHAR *)calloc(templLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, templLocObj->value.stringValue.UTF8Characters, templLocObj->value.stringValue.UTF8Length,
		templLocStr ,templLocObj->value.stringValue.UTF8Length);
	_bstr_t templateLocation(templLocStr);

	WCHAR *defSaveLocStr = (WCHAR *)calloc(defSaveLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, defSaveLocObj->value.stringValue.UTF8Characters, defSaveLocObj->value.stringValue.UTF8Length,
		defSaveLocStr ,defSaveLocObj->value.stringValue.UTF8Length);
	_bstr_t defaultSaveLocation(defSaveLocStr);

	newDoc->CreateNewDocument(templateLocation, defaultSaveLocation);

	free(templLocStr);
	free(defSaveLocStr);
	TRACE("Exiting CreateNewDocument");

	return true;
}

bool CreateNewDocument2(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered CreateNewDocument2");
	if(argCount != 3)
		return false;

	IOWSNewDocument2* newDoc = GetOWSObject<IOWSNewDocument2>(object);
	if(!newDoc)
		return false;

	const NPVariant* templLocObj = &args[1];
	if(templLocObj->type != NPVariantType_String)
		return false;
	const NPVariant* defSaveLocObj = &args[2];
	if(defSaveLocObj->type != NPVariantType_String)
		return false;

	WCHAR *templLocStr = (WCHAR *)calloc(templLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, templLocObj->value.stringValue.UTF8Characters, templLocObj->value.stringValue.UTF8Length,
		templLocStr ,templLocObj->value.stringValue.UTF8Length);
	_bstr_t templateLocation(templLocStr);

	WCHAR *defSaveLocStr = (WCHAR *)calloc(defSaveLocObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, defSaveLocObj->value.stringValue.UTF8Characters, defSaveLocObj->value.stringValue.UTF8Length,
		defSaveLocStr ,defSaveLocObj->value.stringValue.UTF8Length);
	_bstr_t defaultSaveLocation(defSaveLocStr);

	newDoc->CreateNewDocument2(0, templateLocation, defaultSaveLocation);

	free(templLocStr);
	free(defSaveLocStr);
	TRACE("Exiting CreateNewDocument2");
	return true;
}

bool NewBlogPost(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered NewBlogPost");
	if(argCount != 3)
		return false;

	IOWSNewDocument3* newDoc = GetOWSObject<IOWSNewDocument3>(object);
	if(!newDoc)
		return false;

	const NPVariant* provIdObj = &args[0];
	if(provIdObj->type != NPVariantType_String)
		return false;
	const NPVariant* blogUrlObj = &args[1];
	if(blogUrlObj->type != NPVariantType_String)
		return false;
	const NPVariant* blogNameObj = &args[2];
	if(blogNameObj->type != NPVariantType_String)
		return false;

	WCHAR *provIdStr = (WCHAR *)calloc(provIdObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, provIdObj->value.stringValue.UTF8Characters, provIdObj->value.stringValue.UTF8Length,
		provIdStr ,provIdObj->value.stringValue.UTF8Length);
	_bstr_t providerId(provIdStr);

	WCHAR *blogUrlStr = (WCHAR *)calloc(blogUrlObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, blogUrlObj->value.stringValue.UTF8Characters, blogUrlObj->value.stringValue.UTF8Length,
		blogUrlStr ,blogUrlObj->value.stringValue.UTF8Length);
	_bstr_t blogUrl(blogUrlStr);

	WCHAR *blogNameStr = (WCHAR *)calloc(blogNameObj->value.stringValue.UTF8Length + 1, sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, blogNameObj->value.stringValue.UTF8Characters, blogNameObj->value.stringValue.UTF8Length,
		blogNameStr ,blogNameObj->value.stringValue.UTF8Length);
	_bstr_t blogName(blogNameStr);

	newDoc->NewBlogPost(providerId, blogUrl, blogName);

	free(provIdStr);
	free(blogUrlStr);
	free(blogNameStr);
	TRACE("Exiting NewBlogPost");
	return true;
}

bool ViewInExcel(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("ViewInExcel: NOT IMPLEMENTED");
	return false;
}

bool PromptedOnLastOpen(ScriptableObject* object, NPNetscapeFuncs *pBrowserFuncs, const uint32_t argCount, const NPVariant *args)
{
	TRACE("Entered PromptedOnLastOpen");
	if(argCount != 0)
		return false;

	IOWSNewDocument2* newDoc = GetOWSObject<IOWSNewDocument2>(object);
	if(!newDoc)
		return false;

	newDoc->PromptedOnLastOpen();

	TRACE("Exiting PromptedOnLastOpen");
	return true;
}

void init_methods()
{
	methods["CheckinDocument"] = CheckinDocument;
	methods["CheckoutDocumentPrompt"] = CheckoutDocumentPrompt;
	methods["DiscardLocalCheckout"] = DiscardLocalCheckout;
	methods["ViewDocument"] = ViewDocument;
	methods["ViewDocument2"] = ViewDocument2;
	methods["ViewDocument3"] = ViewDocument3;
	methods["EditDocument"] = EditDocument;
	methods["EditDocument2"] = EditDocument2;
	methods["EditDocument3"] = EditDocument3;
	methods["CreateNewDocument"] = CreateNewDocument;
	methods["CreateNewDocument2"] = CreateNewDocument2;
	methods["NewBlogPost"] = NewBlogPost;
	methods["ViewInExcel"] = ViewInExcel;
	methods["PromptedOnLastOpen"] = PromptedOnLastOpen;
}

ScriptableMethod GetScriptableMethod(const char* method)
{
	if(methods.empty())
	{
		init_methods();
	}

	method_map_t::iterator iter = methods.find(method);

	return iter != methods.end() ? iter->second : 0;
}
